package dongze.com.ckdj.service;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.NotificationCompat;
import android.util.Log;

import org.greenrobot.greendao.query.LazyList;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import dongze.com.ckdj.R;
import dongze.com.ckdj.dagger.component.DaggerServiceComponent;
import dongze.com.ckdj.dagger.component.ServiceComponent;
import dongze.com.ckdj.dagger.module.ServiceModule;
import dongze.com.ckdj.data.DataLayer;
import dongze.com.ckdj.map.MapUtil;
import dongze.com.ckdj.persistence.dao.DaoSession;
import dongze.com.ckdj.persistence.dao.MassLossUploadRecord;
import dongze.com.ckdj.persistence.dao.MassLossUploadRecordDao;
import dongze.com.ckdj.persistence.dao.NamePlateUploadRecord;
import dongze.com.ckdj.persistence.dao.NamePlateUploadRecordDao;
import dongze.com.ckdj.persistence.dao.ServerUploadTask;
import dongze.com.ckdj.persistence.dao.ServerUploadTaskDao;
import dongze.com.ckdj.persistence.dao.State;
import dongze.com.ckdj.service.uploader.ImageUploader;
import dongze.com.ckdj.service.uploader.Uploader;
import dongze.com.ckdj.service.uploader.UploaderFactory;
import dongze.com.ckdj.util.MyLog;
import dongze.com.ckdj.util.Util;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by fanqwz on 2017/7/31.
 */

public class ImageUploadService extends Service {
    private static final String TAG = ImageUploadService.class.getSimpleName();
    private static final int NOTIFICATION_ID = ImageUploadService.class.hashCode();
    private static final int TASK_SIZE_LIMIT = 20;
    ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(1024));
    Scheduler scheduler = Schedulers.from(executor);
    @Inject
    MapUtil mapUtil;
    @Inject
    DaoSession session;
    ServiceComponent serviceComponent;
    HashMap<Long, TaskWrapper> currentTasks = new HashMap<>();
    private NetState networkType = NetState.NONE;
    android.os.Handler handler;

    private RemoteCallbackList<IImageUploadCallback> mCallbacks = new RemoteCallbackList<>();

    IImageUploadService mBinder = new IImageUploadService.Stub() {

        @Override
        public void addCallback(final IImageUploadCallback callback) throws RemoteException {
            synchronized (mCallbacks) {
                MyLog.d(TAG, "addCallback: ");
                mCallbacks.register(callback);
            }
        }

        @Override
        public void removeCallback(final IImageUploadCallback callback) throws RemoteException {
            MyLog.d(TAG, "removeCallback: ");
            synchronized (mCallbacks) {
                mCallbacks.unregister(callback);
            }
        }

        @Override
        public void addTask(final ImageUploadTask task) throws RemoteException {
            if (task == null)
                return;

            MyLog.d(TAG, "addTask: " + task);
            synchronized (currentTasks) {
                boolean autoStart = Util.ENABLE.equals(Util.getPreference(getApplicationContext(), Util.PREF_AUTO_START, Util.DISABLE));
                boolean force = Util.ENABLE.equals(Util.getPreference(getApplicationContext(), Util.PREF_4G_DOWNLOAD, Util.DISABLE));
                ServerUploadTask t = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(task.getId())).unique();

                if (t == null) {
                    MyLog.e(TAG, "addTask: task id not exist:" + task.getId());
                    return;
                }

                if (autoStart && currentTasks.size() < TASK_SIZE_LIMIT) {
                    MyLog.d(TAG, "addTask: " + task);

                    if (t == null) {
                        MyLog.e(TAG, "addTask: task id not exist");
                        return;
                    }

                    t.getImage().setState(State.Waiting);
                    t.setState(State.Waiting);
                    t.setForce(force);
                    session.getServerUploadTaskDao().update(t);
                    TaskWrapper tw = new TaskWrapper(t);
                    tw.force = force;
                    currentTasks.put(tw.task.getId(), tw);

                    onTaskStateUpdated();

                    synchronized (mCallbacks) {
                        int count = mCallbacks.beginBroadcast();
                        for (int j = 0; j < count; j++) {
                            try {
                                mCallbacks.getBroadcastItem(j).onTaskAdded(task);
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                        mCallbacks.finishBroadcast();
                    }
                } else if (autoStart) {
                    t.setState(State.QUEUED);
                    session.getServerUploadTaskDao().update(t);

                    synchronized (mCallbacks) {
                        int count = mCallbacks.beginBroadcast();
                        for (int j = 0; j < count; j++) {
                            try {
                                mCallbacks.getBroadcastItem(j).onTaskAdded(task);
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                        mCallbacks.finishBroadcast();
                    }
                }
            }
        }

        @Override
        public void setTaskForce(final ImageUploadTask task) throws RemoteException {
            MyLog.d(TAG, "addTaskForce: " + task);
            synchronized (currentTasks) {
                MyLog.d(TAG, "addTaskForce:post " + task);
                TaskWrapper wrapper = currentTasks.get(task.getId());

                if (wrapper != null) {
                    wrapper.force = true;
                    wrapper.task.setForce(true);
                    session.getServerUploadTaskDao().update(wrapper.task);
                    onTaskStateUpdated();
                } else {
                    ServerUploadTask t = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(task.getId())).unique();
                    if (t != null) {
                        t.setForce(true);
                        session.getServerUploadTaskDao().update(t);
                    }
                }
            }
        }

        @Override
        public void reStartTask(final long id) throws RemoteException {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    synchronized (currentTasks) {
                        ServerUploadTask task = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(id)).unique();

                        if (task != null && currentTasks.get(task.getId()) == null) {
                            loadCurrentTask(task);
                            onTaskStateUpdated();
                        } else {
                            MyLog.e(TAG, "reStartTask: id " + id + "not exist or task already in queue");
                        }
                    }
                }
            });
        }

        @Override
        public void cancelTask(final long id) throws RemoteException {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    TaskWrapper tw = null;
                    synchronized (currentTasks) {
                        tw = currentTasks.get(id);
                    }
                    if (tw == null) {
                        tw.cancel();

                        synchronized (mCallbacks) {
                            int count = mCallbacks.beginBroadcast();
                            for (int i = 0; i < count; i++) {
                                try {
                                    mCallbacks.getBroadcastItem(i).onTaskCanceled(new ImageUploadTask(tw));
                                } catch (RemoteException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    } else {
                        ServerUploadTask t = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(id)).unique();
                        if (t != null) {
                            t.setState(State.Undefined);
                            session.getServerUploadTaskDao().update(t);

                            synchronized (mCallbacks) {
                                int count = mCallbacks.beginBroadcast();
                                for (int i = 0; i < count; i++) {
                                    try {
                                        mCallbacks.getBroadcastItem(i).onTaskCanceled(new ImageUploadTask(tw));
                                    } catch (RemoteException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        } else {
                            MyLog.e(TAG, "run: cancel task but id not exist");
                        }
                    }
                }
            });
        }

        @Override
        public ImageUploadTask[] getCurrentTasks() throws RemoteException {
            synchronized (currentTasks) {
                HashMap.Entry<Long, TaskWrapper>[] entries = new Map.Entry[currentTasks.size()];
                currentTasks.entrySet().toArray(entries);
                ImageUploadTask[] rets = new ImageUploadTask[entries.length];
                for (int i = 0; i < entries.length; i++) {
                    rets[i] = new ImageUploadTask(entries[i].getValue());
                }
                return rets;
            }
        }

        @Override
        public ImageUploadTask[] getUnstartTasks() throws RemoteException {
            synchronized (currentTasks) {
                List<ServerUploadTask> tasks = session.getServerUploadTaskDao().queryBuilder().whereOr(ServerUploadTaskDao.Properties.State.eq(State.Undefined.ordinal()),
                        ServerUploadTaskDao.Properties.State.eq(State.INIT.ordinal())).list();
                List<ServerUploadTask> started = new ArrayList<>(currentTasks.size());

                for (ServerUploadTask t : tasks) {
                    if (currentTasks.get(t.getId()) != null) {
                        started.add(t);
                        break;
                    }
                }

                tasks.removeAll(started);
                ImageUploadTask[] rets = new ImageUploadTask[tasks.size()];

                for (int i = 0; i < rets.length; i++) {
                    rets[i] = new ImageUploadTask(tasks.get(i), 0);
                    rets[i].setUploadProgress(0);
                }

                return rets;
            }
        }

        @Override
        public ImageUploadTask[] getHistoryTask(int page, int size) throws RemoteException {
            synchronized (currentTasks) {
                List<ServerUploadTask> tasks = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.State.eq(State.Finished.ordinal())).offset(page).limit(size).list();
                ImageUploadTask[] rets = new ImageUploadTask[tasks.size()];

                for (int i = 0; i < rets.length; i++) {
                    rets[i] = new ImageUploadTask(tasks.get(i), 100);
                }
                return rets;
            }
        }

        @Override
        public long getHistoryTaskCount() throws RemoteException {
            synchronized (currentTasks) {
                return session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.State.eq(State.Finished.ordinal())).count();
            }
        }

        @Override
        public void forceStartDownload() {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    synchronized (currentTasks) {
                        MyLog.d(TAG, "forceStartDownload: ");
                        for (Map.Entry<Long, TaskWrapper> e :
                                currentTasks.entrySet()) {
                            e.getValue().force = true;
                        }
                    }

                    LazyList<ServerUploadTask> list = session.getServerUploadTaskDao().queryBuilder()
                            .where(ServerUploadTaskDao.Properties.Force.eq(false))
                            .listLazy();
                    for (ServerUploadTask t :
                            list) {
                        t.setForce(true);
                        session.getServerUploadTaskDao().update(t);
                    }
                    list.close();

                    list = session.getServerUploadTaskDao().queryBuilder()
                            .whereOr(ServerUploadTaskDao.Properties.State.eq(State.Undefined.hashCode()),
                                    ServerUploadTaskDao.Properties.State.eq(State.INIT.ordinal()))
                            .listLazy();
                    for (ServerUploadTask t :
                            list) {
                        t.setState(State.QUEUED);
                        session.getServerUploadTaskDao().update(t);
                    }
                    list.close();

                    loadCurrentTasks();
                    onTaskStateUpdated();
                }
            });
        }

        @Override
        public void forceStart(final long id) throws RemoteException {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    synchronized (currentTasks) {
                        TaskWrapper tw = currentTasks.get(id);

                        if (tw != null) {
                            tw.force = true;
                            tw.task.setForce(true);
                            session.getServerUploadTaskDao().update(tw.task);
                        } else {
                            ServerUploadTask t = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(id)).unique();

                            if (t != null) {
                                t.setForce(true);
                                session.getServerUploadTaskDao().update(t);
                                loadCurrentTask(t);
                            } else {
                                MyLog.e(TAG, "forceStart: id not exist");
                            }
                        }

                        onTaskStateUpdated();
                    }
                }
            });
        }

        @Override
        public void deleteTask(long id) throws RemoteException {
            synchronized (currentTasks) {
                MyLog.d(TAG, "deleteTask: " + id);
                ServerUploadTask t = session.getServerUploadTaskDao().queryBuilder().where(ServerUploadTaskDao.Properties.Id.eq(id)).unique();

                if (t != null) {
                    if (t.getState() != State.Finished) {
                        MyLog.e(TAG, "deleteTask: trying to delete an unfinished task:" + id);
                        return;
                    }

                    MyLog.d(TAG, "deleteTask: " + t);
                    ImageUploadService.this.deleteTask(t);

                    synchronized (mCallbacks) {
                        int count = mCallbacks.beginBroadcast();
                        for (int i = 0; i < count; i++) {
                            IImageUploadCallback callback = mCallbacks.getBroadcastItem(i);
                            callback.onTaskDeleted(new ImageUploadTask(t, 0));
                        }
                        mCallbacks.finishBroadcast();
                    }
                }
            }
        }

    };

    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 这个监听wifi的打开与关闭，与wifi的连接无关
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
                int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
                MyLog.e(TAG, "wifiState" + wifiState);
                switch (wifiState) {
                    case WifiManager.WIFI_STATE_DISABLED:
//                        APP.getInstance().setEnablaWifi(false);
                        break;
                    case WifiManager.WIFI_STATE_DISABLING:

                        break;
                    case WifiManager.WIFI_STATE_ENABLING:
                        break;
                    case WifiManager.WIFI_STATE_ENABLED:
//                        APP.getInstance().setEnablaWifi(true);
                        break;
                    case WifiManager.WIFI_STATE_UNKNOWN:
                        break;
                    default:
                        break;


                }
            }
            // 这个监听wifi的连接状态即是否连上了一个有效无线路由，当上边广播的状态是WifiManager
            // .WIFI_STATE_DISABLING，和WIFI_STATE_DISABLED的时候，根本不会接到这个广播。
            // 在上边广播接到广播是WifiManager.WIFI_STATE_ENABLED状态的同时也会接到这个广播，
            // 当然刚打开wifi肯定还没有连接到有效的无线
            if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
                Parcelable parcelableExtra = intent
                        .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                if (null != parcelableExtra) {
                    NetworkInfo networkInfo = (NetworkInfo) parcelableExtra;
                    NetworkInfo.State state = networkInfo.getState();
                    boolean isConnected = state == NetworkInfo.State.CONNECTED;// 当然，这边可以更精确的确定状态
                    MyLog.e(TAG, "isConnected" + isConnected);
//                    if (isConnected) {
//                        APP.getInstance().setWifi(true);
//                    } else {
//                        APP.getInstance().setWifi(false);
//                    }
                }
            }
            // 这个监听网络连接的设置，包括wifi和移动数据的打开和关闭。.
            // 最好用的还是这个监听。wifi如果打开，关闭，以及连接上可用的连接都会接到监听。见log
            // 这个广播的最大弊端是比上边两个广播的反应要慢，如果只是要监听wifi，我觉得还是用上边两个配合比较合适
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                ConnectivityManager manager = (ConnectivityManager) context
                        .getSystemService(Context.CONNECTIVITY_SERVICE);
                MyLog.i(TAG, "CONNECTIVITY_ACTION");

                NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
                if (activeNetwork != null) { // connected to the internet
                    if (activeNetwork.isConnected()) {
                        if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                            // connected to wifi
//                            APP.getInstance().setWifi(true);
                            MyLog.e(TAG, "当前WiFi连接可用 ");
                            networkType = NetState.WIFI;
                            onNetworkStateChanged(networkType);
                        } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                            // connected to the mobile provider's data plan
//                            APP.getInstance().setMobile(true);
                            MyLog.e(TAG, "当前移动网络连接可用 ");
                            networkType = NetState.MOBILE;
                            onNetworkStateChanged(networkType);
                        }
                    } else {
                        MyLog.e(TAG, "当前没有网络连接，请确保你已经打开网络 ");
                        networkType = NetState.NONE;
                        onNetworkStateChanged(networkType);
                    }


                    MyLog.e(TAG, "info.getTypeName()" + activeNetwork.getTypeName());
                    MyLog.e(TAG, "getSubtypeName()" + activeNetwork.getSubtypeName());
                    MyLog.e(TAG, "getState()" + activeNetwork.getState());
                    MyLog.e(TAG, "getDetailedState()"
                            + activeNetwork.getDetailedState().name());
                    MyLog.e(TAG, "getDetailedState()" + activeNetwork.getExtraInfo());
                    MyLog.e(TAG, "getType()" + activeNetwork.getType());
                } else {   // not connected to the internet
                    MyLog.e(TAG, "当前没有网络连接，请确保你已经打开网络 ");
//                    APP.getInstance().setWifi(false);
//                    APP.getInstance().setMobile(false);
//                    APP.getInstance().setConnected(false);

                }

            }
        }
    };

    @Override
    public void onCreate() {
        MyLog.d(TAG, "onCreate: ");
        super.onCreate();
        serviceComponent = DaggerServiceComponent.builder()
                .serviceModule(new ServiceModule(getApplicationContext()))
                .build();
        serviceComponent.inject(this);
        handler = new android.os.Handler(getMainLooper());
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        registerReceiver(mReceiver, intentFilter);

        ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
        if (activeNetwork != null) { // connected to the internet
            if (activeNetwork.isConnected()) {
                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                    MyLog.e(TAG, "当前WiFi连接可用 ");
                    networkType = NetState.WIFI;
                } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                    MyLog.e(TAG, "当前移动网络连接可用 ");
                    networkType = NetState.MOBILE;
                }
            } else {
                MyLog.e(TAG, "当前没有网络连接，请确保你已经打开网络 ");
                networkType = NetState.NONE;
            }
        }

        if (Util.ENABLE.equals(Util.getPreference(getApplicationContext(), Util.PREF_AUTO_START, Util.DISABLE))) {
            loadCurrentTasks();
        }

        onTaskStateUpdated();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        MyLog.d(TAG, "onStartCommand: ");
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        MyLog.d(TAG, "onBind: ");
        return mBinder.asBinder();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(mReceiver);
        mapUtil.dispose();
        super.onDestroy();
    }

    private void loadCurrentTasks() {
        MyLog.d(TAG, "loadCurrentTasks: currentTasks.size()=" + currentTasks.size());
        List<ServerUploadTask> tasks = session.getServerUploadTaskDao().queryBuilder()
                .whereOr(ServerUploadTaskDao.Properties.State.eq(State.QUEUED.ordinal()),
                        ServerUploadTaskDao.Properties.State.eq(State.Waiting.ordinal()),
                        ServerUploadTaskDao.Properties.State.eq(State.Uploading.ordinal()))
                .limit(TASK_SIZE_LIMIT - currentTasks.size()).list();

        List<ServerUploadTask> removeList = new ArrayList<>(tasks.size());

        synchronized (currentTasks) {
            for (ServerUploadTask t :
                    tasks) {
                if (currentTasks.get(t.getId()) != null) {
                    removeList.add(t);
                }

            }
            MyLog.d(TAG, "removelist is " + removeList.size());
            tasks.removeAll(removeList);

            for (ServerUploadTask t : tasks) {
                loadCurrentTask(t);
            }
        }
    }

    // put this in sync block
    private void loadCurrentTask(ServerUploadTask t) {
        if (currentTasks.size() <= TASK_SIZE_LIMIT) {
            TaskWrapper tw = new TaskWrapper(t);
            tw.state = State.Waiting;
            currentTasks.put(tw.getTask().getId(), tw);
            MyLog.d(TAG, "loadCurrentTask: add task" + tw);
            t.setState(State.Waiting);
            t.getImage().setState(State.Waiting);
            session.getImageDao().update(t.getImage());
            session.getServerUploadTaskDao().update(t);
        } else {
            t.setState(State.QUEUED);
            session.getServerUploadTaskDao().update(t);
        }

        synchronized (mCallbacks) {
            int count = mCallbacks.beginBroadcast();
            for (int j = 0; j < count; j++) {
                try {
                    mCallbacks.getBroadcastItem(j).onTaskAdded(new ImageUploadTask(t, 0));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void onTaskStateUpdated() {
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
            onNetworkStateChanged(networkType);
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    onNetworkStateChanged(networkType);
                }
            });
        }
    }

    private void onNetworkStateChanged(NetState state) {
        MyLog.d(TAG, "onNetworkStateChanged: " + state);
        if (state == NetState.WIFI) {
            startWork();
            startWorkForce();
        } else if (state == NetState.MOBILE) {
            startWorkForce();
            //testcode
            startWork();
        }

        synchronized (mCallbacks) {
            int count = mCallbacks.beginBroadcast();
            for (int i = 0; i < count; i++) {
                IImageUploadCallback callback = mCallbacks.getBroadcastItem(i);
                try {
                    callback.onNetStateChanged(state.ordinal());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifyTaskStateChanged(TaskWrapper task) {
        synchronized (mCallbacks) {
            int count = mCallbacks.beginBroadcast();
            for (int i = 0; i < count; i++) {
                try {
                    mCallbacks.getBroadcastItem(i).onTaskProgressChanged(new ImageUploadTask(task), task.percent);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void startWork() {
        MyLog.d(TAG, "startWork: ");
        if (checkPermissions()) {
            MyLog.e(TAG, "startWorkForce: permission not granted");
            return;
        }

        synchronized (currentTasks) {
            for (Map.Entry<Long, TaskWrapper> e :
                    currentTasks.entrySet()) {
                if (e.getValue().observable == null && e.getValue().force == false)
                    e.getValue().start();
            }
        }
    }

    private void startWorkForce() {
        MyLog.d(TAG, "startWorkForce: ");
        if (checkPermissions()) {
            MyLog.e(TAG, "startWorkForce: permission not granted");
            return;
        }

        synchronized (currentTasks) {
            for (Map.Entry<Long, TaskWrapper> e :
                    currentTasks.entrySet()) {

                if (e.getValue().observable == null && e.getValue().force == true)
                    e.getValue().start();

                // 在使用移动网络状态，取消没有force标记的任务
                if (e.getValue().force == false && e.getValue().observable != null)
                    e.getValue().cancel();
            }
        }
    }

    private boolean checkPermissions() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED;
    }

    private void makeTaskFailedNotification(NamePlateUploadRecord record) {
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(getApplicationContext())
                .setAutoCancel(true)
                .setContentTitle("铭牌图片上传失败！")
                .setContentText("车架号：" + record.getVin() +
                        "录入时间：" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(record.getTime()) +
                        "用户名：" + record.getUser())
                .setSmallIcon(R.mipmap.dz_logo)
                .build();
        notificationManager.notify(NOTIFICATION_ID, notification);
    }

    private void makeTaskFailedNotification(MassLossUploadRecord record) {
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(getApplicationContext())
                .setAutoCancel(true)
                .setContentTitle("质损图片上传失败！")
                .setContentText("车架号：" + record.getVin() +
                        "图片ID：" + record.getImageIndex() +
                        "录入时间：" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(record.getTime()) +
                        "用户名：" + record.getUser())
                .setSmallIcon(R.mipmap.dz_logo)
                .build();
        notificationManager.notify(NOTIFICATION_ID, notification);
    }

    private void onTaskFailed(ServerUploadTask task) {
        synchronized (mCallbacks) {
            int count = mCallbacks.beginBroadcast();
            for (int i = 0; i < count; i++) {
                try {
                    ImageUploadTask t = new ImageUploadTask(task, 0);
                    mCallbacks.getBroadcastItem(i).onTaskFailed(t);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mCallbacks.finishBroadcast();
        }

        if (UploaderFactory.TYPE_NAME_PLATE.equals(task.getType())) {
            MassLossUploadRecord record = session.getMassLossUploadRecordDao().queryBuilder()
                    .where(MassLossUploadRecordDao.Properties.Id.eq(task.getRecordId()))
                    .unique();

            if (record != null)
                makeTaskFailedNotification(record);
        } else {
            NamePlateUploadRecord record = session.getNamePlateUploadRecordDao().queryBuilder()
                    .where(NamePlateUploadRecordDao.Properties.Id.eq(task.getRecordId()))
                    .unique();

            if (record != null)
                makeTaskFailedNotification(record);
        }
    }

    private void onTaskUploadFinished(ServerUploadTask task) {
        MyLog.d(TAG, "onTaskUploadFinished: " + task);
        synchronized (mCallbacks) {
            int count = mCallbacks.beginBroadcast();
            for (int i = 0; i < count; i++) {
                try {
                    ImageUploadTask t = new ImageUploadTask(task, 100);
                    mCallbacks.getBroadcastItem(i).onTaskFinished(t);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            mCallbacks.finishBroadcast();
        }

        loadCurrentTasks();
        startWork();
    }

    private void deleteTask(ServerUploadTask task) {
        session.getImageDao().delete(task.getImage());
        session.getServerUploadTaskDao().delete(task);
        deleteImage(task.getImage().getFilePath());
        UploaderFactory.deleteTaskRecord(session, task.getRecordId(), task.getType());
    }

    private void deleteImage(String path) {
        Util.deleteFile(path);
    }

    public class TaskWrapper {
        private Uploader uploader;
        private ServerUploadTask task;
        private Observable<ServerUploadTask> observable;
        int percent = 0;
        State state;
        boolean force = false;
        @Inject
        DataLayer dataLayer;

        boolean started = false;
        boolean canceled = false;

        public TaskWrapper(ServerUploadTask t) {
            serviceComponent.inject(this);
            uploader = UploaderFactory.getUploader(scheduler, AndroidSchedulers.mainThread(), mapUtil, dataLayer, t, session, new ImageUploader.ImageProgressListener() {
                @Override
                public void onStart() {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            state = State.Uploading;
                            task.setState(State.Uploading);
                            session.getServerUploadTaskDao().update(task);
                            notifyTaskStateChanged(TaskWrapper.this);
                        }
                    });
                }

                @Override
                public void onPregress(final int progress) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            percent = progress;
                            notifyTaskStateChanged(TaskWrapper.this);
                        }
                    });
                }

                @Override
                public void onFinish() {

                }

                @Override
                public void onFailed() {

                }
            });
            task = t;
        }

        ServerUploadTask getTask() {
            return task;
        }

        public void start() {
            if (started)
                return;

            started = true;
            uploader.observe(new Observer<String>() {

                @Override
                public void onSubscribe(Disposable d) {
                }

                @Override
                public void onNext(String value) {
                }

                @Override
                public void onError(Throwable e) {
                    MyLog.d(TAG, "TaskWrapperon::setObservable::onError: ");
                    e.printStackTrace();
                    synchronized (currentTasks) {
                        TaskWrapper.this.observable = null;
                        state = State.Undefined;
                        task.setState(State.Undefined);
                        session.getServerUploadTaskDao().update(task);
                        currentTasks.remove(TaskWrapper.this.getTask().getId());
                    }
                    onTaskFailed(task);
                }

                @Override
                public void onComplete() {
                    synchronized (currentTasks) {
                        state = State.Finished;
                        MyLog.d(TAG, "onComplete:id=" + TaskWrapper.this.getTask().getId());
                        MyLog.d(TAG, "onComplete:before remove size=" + currentTasks.size());
                        currentTasks.remove(TaskWrapper.this.getTask().getId());
                        MyLog.d(TAG, "onComplete:after remove size=" + currentTasks.size());
                        task.setState(State.Finished);
                        session.getServerUploadTaskDao().update(task);
                    }
                    onTaskUploadFinished(task);
                }
            });

            uploader.start();
        }

        public void cancel() {
            if (canceled)
                return;

            canceled = true;
            uploader.cancel();
            synchronized (currentTasks) {
                currentTasks.remove(TaskWrapper.this.getTask().getId());
                task.setState(State.Undefined);
                session.getServerUploadTaskDao().update(task);
            }
        }

        @Override
        public String toString() {
            return "TaskWrapper:{" + task + "]";
        }
    }

    enum NetState {
        NONE, WIFI, MOBILE
    }
}
